﻿using TrackSystem.Entities.Extensions;
using TrackSystem.IO.Alarm;
using TrackSystem.IO.Data;
using TrackSystem.IO.Models;

namespace TrackSystem.IO.Protocol
{
    /// <summary>
    /// MODBUS通信协议处理类
    /// </summary>
    public class ModbusTCPPtl : ProtocolBase
    {
        /// <summary>
        /// 在MODBU协议下，是否支持21功能
        /// </summary>
        public bool optSupport21Cmd { get; set; }

        /// <summary>
        /// 在MODBU协议下，是否支持3/4功能分离
        /// </summary>
        public bool optSplit3_4Cmd { get; set; }
        
        private readonly int roAddr;
        private readonly int roLength;
        private readonly int rwAddr;
        private readonly int rwLength;

        private int repeatTimes;    //写入操作重复次数
        private int startTicks;
        private ushort MBAP_Header;

        private int[] wReg;     // 内部保存的一张存储读写存储器的表格
        private int wAddrHead;  // 写入缓冲器的起始地址
        private int wAddrTail;  // 写入缓冲器的结束地址
        private int[] wRegShot; // 内部保存的存储器读写存储器快照
        private bool bSendR, bSendW;     // 发送标志
        private bool bSetZero;      // 通讯故障10次置位标志

        private int cur_read_ptr;   // 当前读取指针
        private int cur_read_len;   // 当前读取长度
        private const int MAX_RO_LEN = 100;     // 单次最大读取长度

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="name">只读存储器起始地址</param>
        /// <param name="station">站地址</param>
        /// <param name="roAddr">只读存储器起始地址</param>
        /// <param name="roLength">只读存储器长度</param>
        /// <param name="rwAddr">读写存储器起始地址</param>
        /// <param name="rwLength">读写寄存器长度</param>
        public ModbusTCPPtl(string name, int station, int roAddr, int roLength, int rwAddr, int rwLength) 
            : base()
        {
            Name = name;
            this.Station = station;
            this.roAddr = roAddr;
            this.roLength = roLength;
            this.rwAddr = rwAddr;
            this.rwLength = rwLength;
            repeatTimes = 0;

            wReg = new int[rwLength];
            wAddrHead = -1;
            wAddrTail = -1;
            wRegShot = new int[rwLength];


            NeedInit = true;
            bSendR = false;
            bSendW = false;
            bSetZero = false;

            cur_read_ptr = roAddr;
            cur_read_len = (roLength > MAX_RO_LEN) ? MAX_RO_LEN : roLength;
        }

        /// <summary>
        /// 区分功能码3和4,3读取读写寄存器，4读写只读存储器
        /// </summary>
        public void Enable_Splite3_4()
        {
            optSplit3_4Cmd = true;
            Tx_r = ReadROFrameProc(roAddr, roLength);
        }

        /// <summary>
        /// 生成读取寄存器的通讯帧
        /// </summary>
        /// <returns></returns>
        public override byte[] GenerateReadFrame(out int NeedRecvLen)
        {
            // 上次的发送没有收到正确回复，bSend没有被置位
            startTicks = Environment.TickCount;

             // 上次的发送没有收到正确回复，bSend没有被置位
            if (bSendR)
            {
                Fail_R++;
                // 失败超过10次，成功次数清零
                if (Fail_R > 10) {
                    Succ_R = 0; 

                    if ( !bSetZero)
                    {
                        foreach (string sName in RegNameList)
                        {
                            RegFactory.Instance.SetVal(sName, 0d);
                        }
                        bSetZero = true;
                    }
                }
            }
            bSendR = true;

            // 针对摄像机的特殊情况
            if (rwAddr == 66 && rwLength==1)
            {
                NeedInit = false;
            }

            // 首次访问，读取RW存储器
            if (NeedInit)
            {
                cur_read_ptr = roAddr;                              // 初始化当前读取地址(考虑读取数据超过120个的情况)
                cur_read_len = (roLength > MAX_RO_LEN) ? MAX_RO_LEN : roLength;

                NeedRecvLen = 2 * rwLength + 9;
                return ReadRWFrameProc(rwAddr, rwLength);
            }

            // 访问只读存储器
            // 产生本次读RO存储器命令帧
            if (optSplit3_4Cmd)  // 如果支持3/4功能分开
            {
                Tx_r = ReadROFrameProc(cur_read_ptr, cur_read_len);
            }
            else   // 全部采用4功能读取
            {
                Tx_r = ReadRWFrameProc(cur_read_ptr, cur_read_len);
            }
            NeedRecvLen = 2 * cur_read_len + 9;
            return Tx_r;
        }

        /// <summary>
        /// 根据收到的内容进行处理
        /// </summary>
        /// <returns></returns>
        public override bool ProcessReadFrame(byte[] buffer)
        {
            Lapse = Environment.TickCount - startTicks;

            //保存返回的数据
            int nLen = buffer.Length;
            Rx_r = buffer; 

            // 站号不对
            if (buffer[6] != Station)
                return false;

            // 功能码不对
            if (buffer[7] != 0x03 && buffer[7] != 0x04)
                return false;

            // 数据帧长度不对
            if (buffer[8] > (nLen - 9))
            //if (buffer[2] != (nLen - 5))
                return false;

            int Index = (NeedInit) ? roLength : (cur_read_ptr - roAddr);
            int cur = 0;

            int realLen = buffer[8] + 9;

            for(int i = 9; i < realLen; i += 2)
            {
                int temp = buffer[i] << 8 | buffer[i + 1];
                if ((buffer[i] & 0x80) == 0x80)   // 将无符号的数据转化为有符号的数据
                {
                    temp -= 65536;
                }

                // 首次读写RW存储器，更新wReg中的值
                if (NeedInit)
                    wReg[cur++] = temp;

                // 将收到的数据更新到DataFactory中
                RegFactory.Instance.SetVal(RegNameList[Index++], temp);
            }

            Succ_R++;
            bSetZero = false;
            bSendR = false;     // 正确收到，标志位变为false
            Fail_R = 0;         // 通信成功，则将失败次数清零

            // 成功接收 更新下次读写RO存储器的值 读写长度超过120的情况
            if (((cur_read_ptr - roAddr) + cur_read_len) >= roLength)   // 已经全部读取完成
            {
                cur_read_ptr = roAddr;
                cur_read_len = (roLength > MAX_RO_LEN) ? MAX_RO_LEN : roLength;
            }
            else        // 未全部读取完成
            {
                cur_read_ptr += cur_read_len;
                int remain_len = roLength - (cur_read_ptr - roAddr);
                cur_read_len = (remain_len > MAX_RO_LEN) ? MAX_RO_LEN : remain_len;
            }

            NeedInit = false;
            return true;
        }

        /// <summary>
        /// 生成写入寄存器的通讯帧
        /// </summary>
        /// <returns></returns>
        public override byte[] GenerateWriteFrame(out int NeedRecvLen)
        {
            startTicks = Environment.TickCount;

            // 待写入的数据队列非空 
            if (writeQueue.Count() > 0)
            {
                while (writeQueue.TryDequeue(out AddressAndVal data))
                { 
                    wReg[data.Address] = (int)data.Value;

                    // 计算预写入的数据寄存器范围  头部位置校正
                    if (wAddrHead == -1 || wAddrHead > data.Address)
                        wAddrHead = data.Address;

                    // 计算预写入的数据寄存器范围  尾部位置校正
                    if (wAddrTail == -1 || wAddrTail < data.Address)
                        wAddrTail = data.Address; 
                }
                repeatTimes = 3;    // 写入的重复次数更新为3次
            }

            // 生成写入数据帧
            if (WriteFlag && repeatTimes-- > 0)
            {
                // 上次没有收到正确的返回帧
                if (bSendW)
                {
                    Fail_W++;
                    Succ_W = 0;
                }
                bSendW = true;

                if (optSupport21Cmd)  // 支持同时读写
                {
                    Tx_w = ReadWriteMultiFrameProc(cur_read_ptr, cur_read_len);
                    NeedRecvLen = 2 * roLength + 9;
                }
                else   // 不支持同时读写
                {
                    Tx_w = WriteMultiFrameProc();
                    NeedRecvLen = 12;
                }
                return Tx_w;
            }

            // 如果写入次数超过三次未成功，则将队列清空
            if (repeatTimes <= 0)
            {
                AlarmFactory.Instance.Insert("Write data error, more than three attempts", 
                    AlarmCategory.COMM, Name, 0);

                // 标志位复位 重启下一个批次的读写任务
                WriteFlag = false;
                wAddrHead = -1;
                wAddrTail = -1;
            }

            NeedRecvLen = 0;
            return null;
        }

        /// <summary>
        /// 根据收到的内容进行处理
        /// </summary>
        /// <returns></returns>
        public override bool ProcessWriteFrame(byte[] buffer)
        {
            Lapse = Environment.TickCount - startTicks;

            int nLen = buffer.Length;
            Rx_w = buffer;

            // 站号不对
            if (buffer[6] != Station)
                return false;

            // 功能码不对
            if (buffer[7] != 0x10 && buffer[7] != 0x17 && buffer[7] != 0x06)
                return false;

            if (buffer[7] == 0x10 && buffer.Length < 12)
                return false;

            if (buffer[7] == 0x06 && buffer.Length < 12)
                return false;

            if (buffer[7] == 0x17 && buffer.Length < (2 * roLength + 9))  // 应当是当前读取长度
                return false;

            if (buffer[7] == 0x17)
            {
                // 更新只读存储器
                int Index = cur_read_ptr - roAddr;
                int realLen = buffer[8] + 9;        // 实际帧长度

                for (int i = 9; i < realLen; i += 2)
                {
                    int temp = buffer[i] << 8 | buffer[i + 1];
                    if ((buffer[i] & 0x80) == 0x80)
                    {
                        temp -= 65536;
                    }
                    RegFactory.Instance.SetVal(RegNameList[Index++], temp);
                }

                // 更新当前读RO存储器的位置
                // 成功接收 更新下次读写RO存储器的值
                if (((cur_read_ptr - roAddr) + cur_read_len) >= roLength)   // 已经全部读取完成
                {
                    cur_read_ptr = roAddr;
                    cur_read_len = (roLength > MAX_RO_LEN) ? MAX_RO_LEN : roLength;
                }
                else        // 未全部读取完成
                {
                    cur_read_ptr += cur_read_len;
                    int remain_len = roLength - (cur_read_ptr - roAddr);
                    cur_read_len = (remain_len > MAX_RO_LEN) ? MAX_RO_LEN : remain_len;
                }
            }

            // 需要把改变的值更新到DataFactory中去 ------------------------
            int idx = wAddrHead + roLength;
            for (int i= wAddrHead; i<=wAddrTail; i++)
            {
                RegFactory.Instance.SetVal(RegNameList[idx++], wRegShot[i]);
            }

            Succ_W++;
            bSendW = false;
            Fail_W = 0;  // 通信成功，则将失败次数清零

            WriteFlag = false;
            repeatTimes = 0;

            // 写成功，则将读写范围复位
            wAddrHead = -1;
            wAddrTail = -1;

            return true;
        }

        /// <summary>
        /// 改变变量的值
        /// </summary>
        /// <param name="Index"></param>
        /// <param name="Value"></param>
        public override void PushRegValue(int Index, double Value)
        {
            Index -= roLength;  //寄存器存储顺序从0开始，先是只读存储器，后面是读写存储器

            if (Index >= 0 && (Index < rwLength))
            {
                writeQueue.Enqueue(new AddressAndVal(Index, Value));
                WriteFlag = true;
            }
        }

        /// <summary>
        /// 获取读写寄存器的值
        /// </summary>
        /// <param name="Adr"></param>
        /// <param name="Len"></param>
        /// <returns></returns>
        private byte[] ReadRWFrameProc(int Adr, int Len)
        {
            byte[] frame = new byte[12];

            MBAP_Header++;
            frame[0] = (byte)(MBAP_Header >> 8);
            frame[1] = (byte)MBAP_Header;

            frame[2] = 0;
            frame[3] = 0;

            frame[4] = 0;
            frame[5] = 0x06;

            frame[6] = (byte)Station;
            frame[7] = 0x03;

            frame[8] = (byte)(Adr >> 8);
            frame[9] = (byte)Adr;
            frame[10] = (byte)(Len >> 8);
            frame[11] = (byte)Len;

            return frame;
        }

        /// <summary>
        /// 获取只读寄存器的值
        /// </summary>
        /// <param name="Adr"></param>
        /// <param name="Len"></param>
        /// <returns></returns>
        private byte[] ReadROFrameProc(int Adr, int Len)
        {
            byte[] frame = new byte[12];

            MBAP_Header++;
            frame[0] = (byte)(MBAP_Header >> 8);
            frame[1] = (byte)MBAP_Header;

            frame[2] = 0;
            frame[3] = 0;

            frame[4] = 0;
            frame[5] = 0x06;

            frame[6] = (byte)Station;
            frame[7] = 0x04;

            frame[8] = (byte)(Adr >> 8);
            frame[9] = (byte)Adr;
            frame[10] = (byte)(Len >> 8);
            frame[11] = (byte)Len;

            return frame;
        }

        /// <summary>
        /// 写寄存器的值
        /// </summary>
        /// <returns></returns>
        private byte[] WriteMultiFrameProc()
        {
            int adr = rwAddr + wAddrHead;
            int regLen = wAddrTail - wAddrHead + 1;
            MBAP_Header++;

            byte[] frame;
            if (regLen == 1)
            {
                frame = new byte[12];

                MBAP_Header++;
                frame[0] = (byte)(MBAP_Header >> 8);
                frame[1] = (byte)MBAP_Header;

                frame[2] = 0;
                frame[3] = 0;
                frame[4] = 0;
                frame[5] = 6;
                frame[6] = (byte)Station;
                frame[7] = 0x06;

                frame[8] = (byte)(adr >> 8);
                frame[9] = (byte)adr;
                frame[10] = (byte)(wReg[wAddrHead] >> 8 );
                frame[11] = (byte)wReg[wAddrHead];

                wRegShot[wAddrHead] = wReg[wAddrHead];
                return frame;
            }

            frame = new byte[regLen * 2 + 13];
            frame[0] = (byte)(MBAP_Header >> 8);
            frame[1] = (byte)MBAP_Header;

            frame[2] = 0;
            frame[3] = 0;

            int MBAP_Length = 2 * regLen + 7;
            frame[4] = (byte)(MBAP_Length >> 8);
            frame[5] = (byte)MBAP_Length;

            frame[6] = (byte)Station;
            frame[7] = 0x10;
            frame[8] = (byte)(adr >> 8);
            frame[9] = (byte)adr;
            frame[10] = (byte)(regLen >> 8);
            frame[11] = (byte)regLen;
            frame[12] = (byte)(2 * regLen);

            int index = 13;
            for (int i = wAddrHead; i <= wAddrTail; i++)
            {
                frame[index++] = (byte)(wReg[i] >> 8);
                frame[index++] = (byte)wReg[i];
                wRegShot[i] = wReg[i];      // 保存读写存储器值的快照
            }

            return frame;
        }

        /// <summary>
        /// 读写寄存器的值
        /// </summary>
        /// <returns></returns>
        private byte[] ReadWriteMultiFrameProc(int roAdr, int roLen)
        {
            int rwAdr = rwAddr + wAddrHead;
            int rwLen = wAddrTail - wAddrHead + 1;

            byte[] frame = new byte[rwLen * 2 + 17];

            MBAP_Header++;
            frame[0] = (byte)(MBAP_Header >> 8);
            frame[1] = (byte)MBAP_Header;

            frame[2] = 0;
            frame[3] = 0;

            int MBAP_Length = 2 * rwLen + 11;
            frame[4] = (byte)(MBAP_Length >> 8);
            frame[5] = (byte)MBAP_Length;

            frame[6] = (byte)Station;
            frame[7] = 0x17;

            // Read Section
            frame[8] = (byte)(roAdr >> 8);
            frame[9] = (byte)roAdr;
            frame[10] = (byte)(roLen >> 8);
            frame[11] = (byte)roLen;

            // Write Section

            frame[12] = (byte)(rwAdr >> 8);
            frame[13] = (byte)rwAdr;
            frame[14] = (byte)(rwLen >> 8);
            frame[15] = (byte)rwLen;
            frame[16] = (byte)(2 * rwLen);

            int index = 17;
            for (int i = wAddrHead; i < wAddrTail; i++)
            {
                frame[index++] = (byte)(wReg[i] >> 8);
                frame[index++] = (byte)wReg[i];
                wRegShot[i] = wReg[i];
            }

            return frame;
        }
    }

}
